/**
 * Copyright (c) [2021] [tenny]
   [fastify-pithy-session] is licensed under Mulan PSL v2.
   You can use this software according to the terms and conditions of the Mulan PSL v2. 
   You may obtain a copy of Mulan PSL v2 at:
            http://license.coscl.org.cn/MulanPSL2 
   THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.  
   See the Mulan PSL v2 for more details. 
 */
const fp = require('fastify-plugin');
const util = require('./lib/util');

function SessionPlugin(fastify, opts, done) {
  opts = formatOption(opts); // 初始化参数

  fastify.decorateRequest('prevSessionHash', ''); // 读取之前的内容hash，用于保存时比较验证是否需要重新保存
  fastify.decorateRequest('session', null); // 保存解析后的session值
  fastify.decorateRequest('prevSessionExpire', null); // 解析时的 session 的过期时间信息

  fastify.addHook('onRequest', onRequest); // 在请求时解析session
  fastify.addHook('onSend', onSend); // 返回数据时，如果 session 有变化则重新刷新 session
  done();

  function onRequest(req, reply, done) {
    const cookie = req.cookies[opts.key]; // 解析 cookie
    let value = {}; // 解析后的session值
    let hash = '';
    if (cookie && cookie.trim().length > 0) {
      try {
        let sessionInfo = util.parseSession(util.decode(req.unsignCookie(cookie).value));
        value = sessionInfo.value;
        req.prevSessionExpire = sessionInfo.expire;
        hash = util.hash(value);
      } catch (err) {}
    }
    // 使用代理包裹，支持动态赋值
    req.session = value;
    req.prevSessionHash = hash;
    done();
  }

  function onSend(req, reply, payload, done) {
    const session = req.session;
    let hash = '';
    if (Object.keys(session).length > 0) {
      hash = util.hash(JSON.stringify(session));
    }
    let changed = hash !== req.prevSessionHash;
    if (opts.rolling) changed = true;
    if (opts.renew && req.prevSessionExpire) {
      const expire = req.prevSessionExpire.expire;
      const maxAge = req.prevSessionExpire.maxAge;
      if (expire && maxAge && expire - parseInt(Date.now() / 1000, 10) < maxAge / 2) {
        changed = true;
      }
    }
    if (changed) {
      // 重新保存数据
      if (opts.renew && opts.cookie.maxAge > 0) {
        session.symbol__maxAge = opts.cookie.maxAge;
        session.symbol__expire = opts.cookie.maxAge + parseInt(Date.now() / 1000, 10);
      }
      reply.setCookie(opts.key, util.encode(session), opts.cookie);
    }
    done(null, payload);
  }
}

/**
 * 检查和格式化 session 配置参数
 * @param {Object} opts 传递的session参数
 * @return {Object} 返回格式化后的session参数
 *
 * @api private
 */
function formatOption(opts = {}) {
  opts.key = opts.key || 'fastify_sess'; // 存储在 cookie 中的 key 字段
  opts.rolling = opts.rolling || false; // 每一次都刷新存储，这样保证频繁使用时不会过期
  opts.renew = opts.renew || false; // 在快要过期的时候，重新刷新存储，重置过期时间
  opts.cookie = {
    maxAge: 86400, // 到期时间, 一天
    httpOnly: true,
    signed: true, // 对 cookie 进行签名，避免被客户端截取
    ...(opts.cookie || {}),
  };
  opts.encode = typeof opts.encode !== 'function' && util.encode;
  opts.decode = typeof opts.decode !== 'function' && util.decode;
  return opts;
}

module.exports = fp(SessionPlugin, { fastify: '3.x', name: 'fastify-session' });
